/*******************************************************************/
/*    Copyright (c) 1999-2020 by Spatial Corp.                     */
/*    All rights reserved.                                         */
/*    Protected by U.S. Patents 5,257,205; 5,351,196; 6,369,815;   */
/*                              5,982,378; 6,462,738; 6,941,251    */
/*    Protected by European Patents 0503642; 69220263.3            */
/*    Protected by Hong Kong Patent 1008101A                       */
/*******************************************************************/

//----------------------------- IopAcisImportMP.cpp -------------------------------

#include <iostream>
#include <fstream>
#include <vector>
#include <time.h>

//Acis
#include "lists.hxx"
#include "kernapi.hxx" 

//Iop
#include "SPAIop.h"
#include "SPAIopAcis.h"
#include "SPAIopMisc.h"
#include "SPAIopVisu.h"
#include "SPAIopPolicyOptions.h"
#include "SPAIopPolicyFactory.h"
#include "SPAIopArgParser.h"
#include "SPAIopSampleUtilities.h"
#include "SPAIopAcisSampleUtilities.h"
#include "SPAIopSampleResultUtils.h"

#include "SPAIopVisuImportResult.h"
#include "SPAIopMultiProcessBatchDefinition.h"
#include "SPAIopMultiProcessSpoolerEx.h"

#include "SPAIopPMIData.h"
#include "SPAIopMiscData.h"
#include "SPAIopMappingData.h"
#include "SPAIopMfgData.h"
#include "SPAIopEntityData.h"
#include "SPAIopPerformanceStatistics.h"

using namespace SPAIopSampleLogging;
using namespace SPAIopAcisPrintUtils;

//-----------------------------------------------------------------------------------------

bool ImportProductStructure(const wchar_t*& ipSourceFilePath, const SPAIopPolicy& iPolicy, SPAIopInputProductStructure& oInputPS, SPAIopPSImportResult &oInputPSResult);
int ImportPart(SPAIopInputPSReference& iInputReference, const SPAIopPolicy& iPolicy, ENTITY_LIST& oAcisEntities, SPAIopPartImportResult& oPartImportResult);
void ImportVisu(SPAIopPartDocument& iAcisInteropPartDocument, const SPAIopPolicy& iPolicy, SPAIopVisuData& oVisuData);
size_t GetNonMissingParts(SPAIopInputProductStructure& iInputPSStructure, SPAIopPSImportResult &iInputPSResult);

//-----------------------------------------------------------------------------------------

class ProgressReporter;
ProgressReporter* _g_pProgressReporter = NULL;

// License file
#include "spatial_license.h"
#include "spa_unlock_result.hxx"
static bool _g_doingMechanical = true;
static bool _g_doingVisualization = false;
static int _g_numProcessors = -1;	// -1 says use them all
static int _g_timeout = -1;
static SPAIopPolicyDataProcessingModeType _g_dataProcessingMode = SPAIopPolicyDataProcessingMode_Unknown;
static std::vector<ENTITY_LIST*> toBeReleased;

//-----------------------------------------------------------------------------------------

void PrintHelp()
{
	PrintMessage(L"Usage: IopAcisImportMP -i <full-input-file-path> -data <minimal|standard> [-n <num procs>][-p <full-policy-file-path>] [-log <full-log-file-path>] [-b <brep>] [-v <visualization>] [-pt <comma separated IDs] [-timeout <block time in milliseconds>] [-h] \n");
	PrintMessage(L"\t-i		: The absolute path to the input file. \n");
	PrintMessage(L"\t-data:	: The data processing mode to use for the translation. Set the value to 'minimal' or 'standard'. \n");
	PrintMessage(L"\t\tminimal - suppresses processing of non-BRep data - may improve performance for BRep-only translations. \n");
	PrintMessage(L"\t\tstandard - allows processing of non-BRep data - necessary for translations that require associated data. \n");
	PrintMessage(L"\t-n		: Maximum number of processors to use.\n");
	PrintMessage(L"\t-p		: The absolute path to the policy file used to control optional behavior during operations. A user supplied policy will override the mode defined in the '-data' argument. \n");
	PrintMessage(L"\t-log	: The absolute path to the log file. \n");
	PrintMessage(L"\t-b		: 1 to process the brep (mechanical) data, (1 default).\n");
	PrintMessage(L"\t-v		: 1 to transfer visualization data, (0 default). \n");
	PrintMessage(L"\t-pt	: A comma separated list to denote which UniqueID numbers to use in a partial translation.\n");
	PrintMessage(L"\t-timeout	: Blocks until either the next available reference is done translating or until the timeout is reached.\n");
	PrintMessage(L"\t-h		: This help.\n");
}

//-----------------------------------------------------------------------------------------
class ProgressReporter
{
public:

	//-----------------------------------------------------------------------------------------

	ProgressReporter() : _psDone(false), _numParts(0), nonMissingParts(0), _partsDone(0), _lastProgress(-1) {}

	//-----------------------------------------------------------------------------------------

	void ProgressCallBack(double iProgress, bool& iAbort)
	{
		int progressRange = 100;
		int percent = 0;

		double actualProgressPercent = iProgress * 100;
		int progress = static_cast<int>(actualProgressPercent);

		if (!_psDone)
		{
			percent = (int)((100.0*progress) / double(progressRange));
			PrintMessage(L"Product Structure Progress : %d%%\n", percent);
		}
		else
		{
			size_t cnt = _partsDone * progressRange + progress;
			percent = (int)((100.0*cnt) / (nonMissingParts*progressRange));
			if (percent > _lastProgress)
				PrintMessage(L"Translation Progress : %d%%\n", percent);
			_lastProgress = percent;
			if (progress == progressRange)
				_partsDone += 1;
		}
	}

	//-----------------------------------------------------------------------------------------

	void SetNumParts(int iNumParts)
	{
		_numParts = iNumParts;
	}

	//-----------------------------------------------------------------------------------------

	void setNonMissingParts(size_t iNonMissingParts)
	{
		nonMissingParts = iNonMissingParts;
	}

	//-----------------------------------------------------------------------------------------

	void SetPsDone(bool isDone)
	{
		_psDone = isDone;
	}

	//-----------------------------------------------------------------------------------------

private:

	//-----------------------------------------------------------------------------------------

	bool _psDone;
	size_t _numParts;
	size_t nonMissingParts;
	size_t _partsDone;
	int _lastProgress;

	//-----------------------------------------------------------------------------------------

};

//-----------------------------------------------------------------------------------------

void MyProgressCallBack(double iProgress, bool& iAbort)
{
	if (_g_pProgressReporter != NULL)
	{
		_g_pProgressReporter->ProgressCallBack(iProgress, iAbort);
	}
}

//get the configuration name, if any, from the policy
static bool GetConfigNameFromPolicy(const SPAIopPolicy& iPolicy, SPAIopWString& format, SPAIopWString& oConfigName)
{
	const wchar_t* wFormat = NULL;
	format.ToWChar(wFormat);

	bool retval = true;

	//pull correct options
	if (0 == wcscmp(L"SOLIDWORKS", wFormat))
	{
		SPAIopImportOptionsSolidWorks options;
		if (iPolicy.GetCurrentFormatImportOptions(options))
		{
			options.GetCurrentImportConfiguration(oConfigName);
		}
	}
	else if (0 == wcscmp(L"SWDirect", wFormat))
	{
		SPAIopImportOptionsSolidWorksDirect options;
		if (iPolicy.GetCurrentFormatImportOptions(options))
		{
			options.GetCurrentImportConfiguration(oConfigName);
		}
	}
	else if (0 == wcscmp(L"UG", wFormat))
	{
		SPAIopImportOptionsNX options;
		if (iPolicy.GetCurrentFormatImportOptions(options))
		{
			options.GetCurrentImportConfiguration(oConfigName);
		}
	}
	else if (0 == wcscmp(L"UGDirect", wFormat))
	{
		SPAIopImportOptionsNXDirect options;
		if (iPolicy.GetCurrentFormatImportOptions(options))
		{
			options.GetCurrentImportConfiguration(oConfigName);
		}
	}
	else if (0 == wcscmp(L"PROE", wFormat))
	{
		SPAIopImportOptionsCreo options;
		if (iPolicy.GetCurrentFormatImportOptions(options))
		{
			options.GetCurrentImportConfiguration(oConfigName);
		}
	}
	else
	{
		retval = false;
	}

	return retval;
}

//utility to return only file name with extension
static std::wstring GetFileName(const std::wstring& filePath)
{
	std::wstring fileName = filePath;
	std::size_t pos = fileName.find_last_of(L"/\\");
	if (std::wstring::npos != pos && pos < fileName.length())
	{
		fileName = fileName.substr(pos + 1);
	}

	return fileName;
}

void DoSomethingWithError(SPAIopAcisError& error)
{
	std::wcout << L"User API caught error errorID: " << error.GetErrorId() << std::endl;
	SPAIopWString req = error.GetRequest();
	SPAIopWString diagnostic = error.GetDiagnostic();
	SPAIopWString advice = error.GetAdvice();
	const wchar_t *pReqArray = NULL;
	req.ToWChar(pReqArray);
	std::wcout << "Error Message = " << pReqArray << std::endl;
	const wchar_t *pDiagnosticArray = NULL;
	diagnostic.ToWChar(pDiagnosticArray);
	std::wcout << "Error Diagnostic = " << pDiagnosticArray << std::endl;
	const wchar_t *pAdviceArray = NULL;
	advice.ToWChar(pAdviceArray);
	std::wcout << "Error Advice = " << pAdviceArray << std::endl;
}

int ImportPart(SPAIopPartDocument& iDoc, const SPAIopPolicy& iPolicy, SPAIopPartImportResult& oPartImportResult, ENTITY_LIST& oEntities)
{
	int result = SPAIopSampleResult_Import_Ok;

	SPAIopAcisPartImporter importer;
	try
	{
		oPartImportResult = importer.ImportWithPolicy(iPolicy, iDoc, oEntities);
	}
	catch (SPAIopAcisError& error)
	{
		result = SPAIopSampleResult_Import_Part_Fail;
		DoSomethingWithError(error);
		PrintMessage(L"\n FAILURE : Part failed to import mechanical.");
	}

	return result;
}

int ImportPartVisualization(SPAIopPartDocument& iDoc, const SPAIopPolicy& iPolicy, SPAIopVisuImportResult& vizResult)
{
	int result = SPAIopSampleResult_Import_Ok;
	SPAIopVisualizationImporter importer;
	
	try
	{
		vizResult = importer.ImportWithPolicy(iPolicy, iDoc);
	}
	catch (SPAIopAcisError& error)
	{
		result = SPAIopSampleResult_Import_Part_Fail;
		DoSomethingWithError(error);
		PrintMessage(L"\n FAILURE : Part failed to import visualization.");
	}

	return result;
}

//-----------------------------------------------------------------------------------------
template <typename I>
int GetIteratorCount(I iter)
{
	//get the count for an iterator
	// using int to match GetCount methods
	int retVal = 0;

	while (iter.Next())
	{
		++retVal;
	}

	return retVal;
}

int ProcessSinglePart(unsigned int refID, const SPAIopPolicy& policy, SPAIopInputProductStructure& iInputPS, ENTITY_LIST* oPartEntities, SPAIopRep availableRep)
{
	int result = SPAIopSampleResult_Import_Ok;

	//obtain info from the input product structure for this part
	// reference
	SPAIopInputPSReference reference = iInputPS.GetReferenceFromId(refID);
	// part document
	SPAIopPartDocument doc = reference.GetDocument();
	// unique id
	unsigned int uniqueID = reference.GetUniqueID();
	// reference name
	SPAIopWString refName = reference.GetReferenceName();
	// reference type
	SPAIopWString refType = reference.GetDefinitionDocumentType();
	// storage name
	SPAIopWString storageName = reference.GetStorageName();

	//configuration name
	SPAIopWString configName;
	bool gotConfigName = GetConfigNameFromPolicy(policy, refType, configName);

	//Information for stdout
	const wchar_t* wName = NULL;
	refName.ToWChar(wName);
	std::wstring name = wName;
	const wchar_t* wType = NULL;
	refType.ToWChar(wType);
	std::wstring type = wType;
	const wchar_t* wStorage = NULL;
	storageName.ToWChar(wStorage);
	std::wstring storage = wStorage;
	std::wstring storageFileName = GetFileName(wStorage);
	std::wstring config = L"";
	if (gotConfigName)
	{
		const wchar_t* wConfig = NULL;
		configName.ToWChar(wConfig);
		config = wConfig;
	}
	std::wcout << std::endl << "Processing Reference Part. UniqueID = " << uniqueID << ", Reference Name = " << name << ", Type = " << type << ", Storage Name = " << storageFileName << ", Config Name = " << config << "\n";

	//import the translated part
	if (_g_doingMechanical && (availableRep == SPAIopRep_Mechanical || availableRep == SPAIopRep_All))
	{
		//in this sample, we'll read in the acis entities and verify that they exist
		SPAIopPartImportResult importResult;
		int res = ImportPart(doc, policy, importResult, *oPartEntities);
		int entityCount = 0;
		if (NULL != oPartEntities)
		{
			entityCount = oPartEntities->count();
		}
		std::wcout << (res == 0 ? "SUCCESS" : "FAILURE") << ": Obtained " << entityCount << " mechanical entities for reference: " << name << std::endl;

		if (0 != res)
		{
			result = SPAIopSampleResult_Import_Part_Fail;
		}
		else
		{
			//get miscellaneous data through the import result
			//in this sample, we'll print some values to show that we successfully imported the miscellaneous data
			// one could transfer this to output model or display via the individual iterators
			int numPMIAnnotations = importResult.GetPMIData().GetAnnotationTotalCount();
			bool hasMiscData = importResult.GetMiscData().HasData();
			//get the misc data (validating via count)
			int materialsCount = 0;
			int layerCount = 0;
			int layerFilterCount = 0;
			int selectionSetCount = 0;
			int noteParametersCount = 0;
			int userPropertiesCount = 0;
			int bimPropertiesCount = 0;
			int wcsCount = 0;
			if (hasMiscData)
			{
				materialsCount = GetIteratorCount(importResult.GetMiscData().GetMaterialsIterator());
				layerCount = GetIteratorCount(importResult.GetMiscData().GetLayerIterator());
				layerFilterCount = GetIteratorCount(importResult.GetMiscData().GetLayerFilterIterator());
				selectionSetCount = GetIteratorCount(importResult.GetMiscData().GetSelectionSetIterator());
				noteParametersCount = GetIteratorCount(importResult.GetMiscData().GetNoteParametersIterator());
				userPropertiesCount = GetIteratorCount(importResult.GetMiscData().GetUserPropertiesIterator());
				bimPropertiesCount = GetIteratorCount(importResult.GetMiscData().GetBimPropertiesIterator());
				wcsCount = GetIteratorCount(importResult.GetMiscData().GetWCSIterator());
			}
			bool hasMfgData = importResult.GetManufacturingData().HasData();
			//get the manufacturing data (validating via count)
			int counterBoreHoldCount = 0;
			if (hasMfgData)
			{
				importResult.GetManufacturingData().GetFeatureCounterBoreHoleIterator().GetCount();
			}

			std::wcout << "   Number PMI annotations " << numPMIAnnotations << ", has Misc data " << (hasMiscData ? "True " : "False ") <<
				", has MFG data " << (hasMfgData ? "True " : "False ") << std::endl;
			std::wcout << "   Count of materials: " << materialsCount << ", layers: " << layerCount << ", layer filters: " << layerFilterCount << ", selection sets: " << selectionSetCount <<
				", notes: " << noteParametersCount << ", user properties: " << userPropertiesCount << ", bim properties: " << bimPropertiesCount << ", wcs: " << wcsCount << std::endl;
		}
	}

	//import the visualization
	if (_g_doingVisualization && (availableRep == SPAIopRep_Visualization || availableRep == SPAIopRep_All))
	{
		//get the visualization
		SPAIopVisuImportResult vizResult;

		int res = ImportPartVisualization(doc, policy, vizResult);
		std::wcout << (0 == res ? "SUCCESS" : "FAILURE") << ": Obtain visualization for reference: " << name << std::endl;
		if (0 == res)
		{
			//get the visualization data from the visualization import result
			SPAIopVisuData visualization = vizResult.GetVisualizationData();

			//in this sample, we'll print some values to show that we successfully imported the visualization data
			// one could traverse the scene graph, etc.
			SPAIopWString units = SPAIopLengthUnitUtils::LengthUnitToString(visualization.GetUnits());
			const wchar_t* wUnits = NULL;
			units.ToWChar(wUnits);
			std::wstring unit = wUnits;

			int numViews = visualization.GetViewIterator().GetCount();
			int numCaptures = visualization.GetCaptureIterator().GetCount();
			int numSceneRoots = visualization.GetSceneRootChildIterator().GetCount();

			std::wcout << "   Units " << unit << ", view count " << numViews << ", capture count " << numCaptures << ", scene roots count " << numSceneRoots << std::endl;
		}
		else
		{
			result = SPAIopSampleResult_Import_Part_Fail;
		}

	}

	return result;
}

//========================================================================================================

void ProcessPart(SPAIopMultiProcessBatchDefinition::Token_t& refToken, SPAIopInputProductStructure& iInputPS, SPAIopInputPSMissingFileIter &iMissingFiles)
{
	// unique id
	unsigned int refID = refToken.GetRefID();
	// policy for this part
	const SPAIopPolicy policy = refToken.GetPolicy();
	//representations this refToken processes
	SPAIopRep availableRep = refToken.GetImportedRepresentation();

	//process this part
	SPAIopInputPSReference reference = iInputPS.GetReferenceFromId(refID);
	if (iMissingFiles.ContainsPath(reference.GetStorageName()))
	{
		return;
	}
	ENTITY_LIST* partEntities = ACIS_NEW ENTITY_LIST;
	ProcessSinglePart(refID, policy, iInputPS, partEntities, availableRep);

	//place entities on global entity list to be released at end of sample run
	toBeReleased.push_back(partEntities);
}

//========================================================================================================

int ProcessNextAvailableForPart(SPAIopMultiProcessSpoolerEx& iSpooler, SPAIopInputProductStructure& iInputPS, SPAIopInputPSMissingFileIter &iMissingFiles)
{
	int result = SPAIopSampleResult_Import_Ok;

	// this returns a token containing the unique reference id and the policy, which includes the current
	// configuration name, used for the import
	SPAIopMultiProcessBatchDefinition::Token_t refToken;
	while (iSpooler.WaitForNextAvailableReference(refToken) != SPAIopMultiProcessSpoolerEx::WaitResult_t::NoMoreData)
	{
		ProcessPart(refToken, iInputPS, iMissingFiles);
	}

	return result;
}

//========================================================================================================

int ProcessNextAvailableForPartWithTimeout(SPAIopMultiProcessSpoolerEx& iSpooler, SPAIopInputProductStructure& iInputPS, SPAIopInputPSMissingFileIter &iMissingFiles)
{
	int result = SPAIopSampleResult_Import_Ok;

	// this returns a token containing the unique reference id and the policy, which includes the current
	// configuration name, used for the import
	SPAIopMultiProcessBatchDefinition::Token_t refToken;
	typedef SPAIopMultiProcessSpoolerEx::WaitResult_t WaitResult;
	WaitResult waitResult;
	while ((waitResult = iSpooler.WaitForNextAvailableReference(refToken, _g_timeout)) != WaitResult::NoMoreData)
	{
		if (waitResult == WaitResult::TimedOut)
		{
			PrintMessage(L"Timeout, terminating\n");
			return SPAIopSampleResult_Import_Part_Fail;
		}

		ProcessPart(refToken, iInputPS, iMissingFiles);
	}

	return result;
}

//========================================================================================================

int ImportModel(const wchar_t*& ipSourceFilePath, SPAIopPolicy& iPolicy, const std::vector<int> &iPartialTransParts)
{
	int returnValue = SPAIopSampleResult_Import_Ok;

	_g_pProgressReporter = new ProgressReporter();

	// Import Input Product Structure 
	SPAIopInputProductStructure inputPS;
	SPAIopPSImportResult inputPSResult;
	bool gotPS = ImportProductStructure(ipSourceFilePath, iPolicy, inputPS, inputPSResult);

	if (!gotPS)
	{
		// If the product structure can't be loaded there's no way to translate anything
		return SPAIopSampleResult_Import_PS_Fail;
	}

	//create and initialize a multi-process spooler to translate input parts
	// create batch definition
	SPAIopMultiProcessBatchDefinition batchDef(inputPS, iPolicy);

	//For Partial Translation
	if (iPartialTransParts.size() > 0)
	{
		batchDef.ClearPSForPartialTranslation();
		size_t count = iPartialTransParts.size();
		for (size_t i = 0; i < count; i++) 
		{
			try
			{
				SPAIopInputPSReference partUniqueRef = inputPS.GetReferenceFromId(iPartialTransParts[i]);
				batchDef.AddPart(partUniqueRef, iPolicy);
			} 
			catch (SPAIopAcisError&) 
			{
				PrintMessage(L"Invalid Part Id: %d\n", (iPartialTransParts[i]));
			}
		}
	}

	//set maximum number of processors to use
	if (_g_numProcessors > 0)
	{
		batchDef.SetNumberOfProcessors(_g_numProcessors);
	}

	// set processing type(s)
	SPAIopRep rep = SPAIopRep::SPAIopRep_None;
	if (_g_doingMechanical && _g_doingVisualization)
	{
		rep = SPAIopRep::SPAIopRep_All;
	}
	else if (_g_doingMechanical)
	{
		rep = SPAIopRep::SPAIopRep_Mechanical;
	}
	else if (_g_doingVisualization)
	{
		rep = SPAIopRep::SPAIopRep_Visualization;
	}

	//if nothing chosen for translation return error
	if (SPAIopRep::SPAIopRep_None == rep)
	{
		std::wcout << "ERROR: Nothing chosen to translate" << std::endl;
		return SPAIopSampleResult_Export_PS_Fail;
	}
	batchDef.AddType(rep);

	// initialize spooler and start conversion
	SPAIopMultiProcessSpoolerEx spooler(batchDef);

	bool didStart = spooler.InitiateConversion();

	// if spooler couldn't initialize, then fail
	if (!didStart)
	{
		std::wcerr << "ERROR: The conversion was not initiated in parallel" << std::endl;
		returnValue = SPAIopSampleResult_Export_PS_Fail;
	}

	std::wcout << "Reference count: " << inputPS.GetUniqueReferencesCount() << std::endl;

	//Process each part (once) as it becomes available
	SPAIopInputPSMissingFileIter missingFiles = inputPSResult.GetMissingFileIterator();
	if (_g_timeout == -1)
		ProcessNextAvailableForPart(spooler, inputPS, missingFiles);
	else
		ProcessNextAvailableForPartWithTimeout(spooler, inputPS, missingFiles);


	return returnValue;
}

//-----------------------------------------------------------------------------------------

bool ImportProductStructure(const wchar_t*& ipSourceFilePath, const SPAIopPolicy& iPolicy, SPAIopInputProductStructure& oInputPS, SPAIopPSImportResult &oInputPSResult)
{
	bool returnValue = true;
	// Import the Assembly tree as a Product Structure.  Note that 
	// importing a Product Structure does not import any brep data associated
	// with the model, which is done separately from part-associated reference nodes
	// in the Product Structure.  Use try/catch exception handling blocks to 
	// manage fatal errors.

	// Create Source Document from input file
	std::wstring sourceFilePath(ipSourceFilePath);
	SPAIopWString wSourceFilePath(sourceFilePath.c_str());
	SPAIopDocument sourceDoc(wSourceFilePath);

	// Create a Product Strcuture Importer
	SPAIopProductStructureImporter psImporter;

	try
	{
		psImporter.SetProgressCallBack((SPAIopProgressCallback)MyProgressCallBack);

		oInputPSResult = psImporter.ImportWithPolicy(iPolicy, sourceDoc, oInputPS);
		SPAIopLengthUnitType inputPSUnit = oInputPS.GetUnits();
		if (SPAIopLengthUnit_Unknown != inputPSUnit)
		{
			SPAIopWString unitStr = SPAIopLengthUnitUtils::LengthUnitToString(inputPSUnit);
			const wchar_t * pUnitArray = NULL;
			unitStr.ToWChar(pUnitArray);
			std::wcout << "Product Structure Units: " << pUnitArray << std::endl;
		}

		DumpMissingFilesInProductStructure(oInputPSResult);

		if (_g_pProgressReporter != NULL)
		{
			_g_pProgressReporter->SetNumParts(oInputPS.GetUniqueReferencesCount());
			size_t numNonMissingParts = GetNonMissingParts(oInputPS, oInputPSResult);
			_g_pProgressReporter->setNonMissingParts(numNonMissingParts);
			_g_pProgressReporter->SetPsDone(true);
		}
	}
	catch (SPAIopAcisError& error)
	{
		DisplayError(error);
		returnValue = false;
	}
	catch (...)
	{
		std::wcout << "FAILURE: Unknown exception caught! " << std::endl;
		returnValue = false;
	}

	return returnValue;
}
//-----------------------------------------------------------------------------------------

size_t GetNonMissingParts(SPAIopInputProductStructure& iInputPSStructure, SPAIopPSImportResult &iInputPSResult)
{
	size_t missingParts = 0;
	size_t totalParts = iInputPSStructure.GetUniqueReferencesCount();
	SPAIopInputPSMissingFileIter missingFiles = iInputPSResult.GetMissingFileIterator();
	int nInstances = iInputPSStructure.GetUniqueReferencesCount();
	if (nInstances > 0)
	{
		int i = 0;
		for (i = 0; i < nInstances; i++)
		{

			SPAIopInputPSReference uniqueReference = iInputPSStructure.GetUniqueReference(i);
			SPAIopWString referenceStorageName = uniqueReference.GetStorageName();
			bool isMissingReference = missingFiles.ContainsPath(referenceStorageName);
			if (true == isMissingReference)
			{
				if (uniqueReference.IsPart())
				{
					missingParts++;
				}

			}
		}

	}
	return (totalParts - missingParts);

}

//-----------------------------------------------------------------------------------------

void ImportVisu(SPAIopPartDocument& iAcisInteropPartDocument, const SPAIopPolicy& iPolicy, SPAIopVisuData& oVisuData)
{
	// Import the visualization data of a part into the given SPAIopVisuData using the Import 
	// method of a SPAIopVisualizationImporter object.  Use try/catch exception handling blocks to 
	// manage fatal errors.

	SPAIopVisualizationImporter visuImporter;
	try
	{
		SPAIopVisuImportResult importRes = visuImporter.ImportWithPolicy(iPolicy, iAcisInteropPartDocument);

		oVisuData = importRes.GetVisualizationData();
	}
	catch (SPAIopAcisError& error)
	{
		DisplayError(error);
	}
	catch (...)
	{
		std::wcout << "FAILURE: Unknown exception caught! " << std::endl;
	}
}

//-----------------------------------------------------------------------------------------

int test_IopAcisImportMP(int argc, char* argv[])
{
	int result(1);
	SPAIopPerformanceStatistics printPerfoStatsObj;

	// Work-flow similar to Acis based app where Acis will be generally initialized before IOP is called.
	api_start_modeller(0);

	try
	{
		// IOP is called in its own scope.
		// This sample uses a guard class that initializes InterOp upon 
		// construction and terminates InterOp upon destruction.  All 
		// translations are done within the boundaries of a single 
		// initialization and shutdown.  InterOp may only be initialized 
		// and shutdown once per process.  Please do not use the guard 
		// classes multiple times per process.
		SPAIopAcisSystemInitGuard initGuard(SPATIAL_LICENSE);

		result = initGuard.GetInitStatus();

		if (SPA_UNLOCK_NO_MSG == result)
		{
			// Spatial products have been unlocked
			std::wcout << L"SUCCESS: SPAIopAcisSystem::Initialize. " << std::endl;
		}
		else if (SPA_UNLOCK_KEY_WARN_EXPIRE == result)
			std::wcout << L"SPAIopAcisSystem::Initialize returned SPA_UNLOCK_KEY_WARN_EXPIRE; your license key will expire soon!\n";
		else if (SPA_UNLOCK_REGISTRATION_WARN_EXPIRE == result)
			std::wcout << L"SPAIopAcisSystem::Initialize returned SPA_UNLOCK_REGISTRATION_WARN_EXPIRE; your registration will expire soon.\n";
		else
		{
			std::wcout << L"SPAIopAcisSystem::Initialize returned an unexpected value; Contact Spatial Customer Support or your Account Manager.\n";
			return 1;
		}
		result = 1;

		char* argFields[] = { "i", "data", "n", "p", "b", "v", "pt", "timeout","h", "log" };
		int size = sizeof(argFields) / sizeof(*argFields);
		SPAIopArgParser parser(size, argFields, argc, argv, &PrintHelp);

		if (true == parser.IsSuccess())
		{
			const wchar_t* pSourceFilePath = NULL;
			bool hasInputFileName = parser.GetWStringValueFor("i", pSourceFilePath);

			const wchar_t* pDataProcessingMode = NULL;
			bool hasDataProcessingMode = parser.GetWStringValueFor("data", pDataProcessingMode);

			int nprocFlag = 0;
			bool hasNprocFlag = parser.GetIntegerValueFor("n", nprocFlag);

			const wchar_t* pPolicyFilePath = NULL;
			bool hasPolicyFileName = parser.GetWStringValueFor("p", pPolicyFilePath);

			const wchar_t* pLogFilePath = NULL;
			bool hasLogFileName = parser.GetWStringValueFor("log", pLogFilePath);

			int brepFlag = 0;
			bool hasBrepFlag = parser.GetIntegerValueFor("b", brepFlag);

			int visuFlag = 0;
			bool hasVisuFlag = parser.GetIntegerValueFor("v", visuFlag);

			std::vector<int> partialTransParts;														//New
			bool hasPartialTransFlag = parser.GetArrayValueFor("pt", partialTransParts);

			int timeout = 0;
			bool hasTimeout = parser.GetIntegerValueFor("timeout", timeout);

			if (hasDataProcessingMode && pDataProcessingMode)
			{
				if (0 == wcscmp(L"minimal", pDataProcessingMode))
				{
					_g_dataProcessingMode = SPAIopPolicyDataProcessingMode_Minimal;
				}
				else if (0 == wcscmp(L"standard", pDataProcessingMode))
				{
					_g_dataProcessingMode = SPAIopPolicyDataProcessingMode_Standard;
				}
				else
				{
					hasDataProcessingMode = false;
				}
			}
			else
			{
				std::wcout << L"WARNING: Data processing mode is a required option. \n";
				PrintHelp();
				return result;
			}				

			if (hasNprocFlag && (nprocFlag > 0))
			{
				_g_numProcessors = nprocFlag;
			}

			if (hasBrepFlag && (brepFlag != 1))
			{
				_g_doingMechanical = false;
			}

			if (hasVisuFlag && (visuFlag != 0))
			{
				_g_doingVisualization = true;
			}

			if (hasTimeout && timeout > 0)
			{
				_g_timeout = timeout;
			}

			if (hasInputFileName && pSourceFilePath)
			{
				
				SPAIopPolicy policy;
				if (0 != pPolicyFilePath)
				{
					SPAIopWString policyFilePath(pPolicyFilePath);
					SPAIopPolicyFileStreamXML xmlPolicyStream;
					xmlPolicyStream.SetUnstreamFilePath(policyFilePath);

					policy.Deserialize(xmlPolicyStream);
				}
				else if (hasDataProcessingMode)
				{
					policy = SPAIopPolicyFactory::BuildPolicyDataProcessingMode(_g_dataProcessingMode);
				}
				else
				{
					std::wcout << L"WARNING: Neither a valid policy file was given or a valid data processing mode was defined. The translation will proceed with the default translation behavior. \n";
				}

				SetOrUpdateLogFileInPolicy(pLogFilePath, policy);

				result = ImportModel(pSourceFilePath, policy, partialTransParts);
				// When **completely** done with these entities, release them.
				// This is especially useful when sequentially importing and processing many parts in one session.
				//for (auto&& elist : toBeReleased) // nice, if your compiler supports it
				for (std::vector<ENTITY_LIST*>::iterator iter = toBeReleased.begin(); iter != toBeReleased.end(); ++iter)
				{
					SPAIopAcisSystem::ReleasePartEntities(**iter);
					ACIS_DELETE *iter;
				}
				toBeReleased.clear();
			}
			else
			{
				std::wcout << L"WARNING: Input file name is a required option. \n";
				PrintHelp();
				return result;
			}			
		}
	}
	catch (SPAIopAcisError& fatalErr)
	{
		DisplayError(fatalErr);
		result = 1;
	}
	catch (...)
	{
		PrintMessage(L"FAILURE: Unknown exception caught! ");
		result = 1;
	}

	api_stop_modeller();

	return result;
}

//-----------------------------------------------------------------------------------------

